import functools,joblib,cPickle as cP,operator as op,os
from collections import namedtuple,defaultdict
from copy import deepcopy

Example = namedtuple("Example","char n_strokes strokes vectors")
Symbol = namedtuple("Symbol","matches bounds")
Parse = namedtuple("Parse","tree score bounds")
Rule = namedtuple("Rule","left op rightlist")
Match = namedtuple("Match","char logp")
Tree = namedtuple("Tree","label bounds subtrees")

HUGE = 1e30
BIG = 1e15

join = os.path.join


def identity(x): return x

def memoized_arg0(fn):
    cache = {}
    @functools.wraps(fn)
    def memo_ver(*args):
        key = joblib.hash(args[0])
        if key in cache: 
            print key,len(args),args[0].size
            return cache[key]
        else:
            obj = cache[key] = fn(*args)
            return obj
    return memo_ver

class Database(object):
    def __init__(self,filename,klass):
        self.klass = klass
        self.data = []
        self.filename = filename        
    def appendrow(self,**kw):
        self.data.append(self.klass(**kw))
    def save(self):
        dump(self,self.filename)
    def __len__(self):
        return len(self.data)
    def __iter__(self):
        return self.data.__iter__()
    def __getitem__(self,key):
        return self.data[key]

def load(filename):
    with open(filename,"r") as fd: return cP.load(fd)

def dump(obj,filename):
    with open(filename,"w") as fd: return cP.dump(obj,fd)
    
def safemax(gen,default = None,key=identity):
    try: return max(gen,key=key)
    except ValueError: return default
    
def disp_args(*args,**kw):
    return ",".join([str(arg) for arg in args] + ["%s=%s"%(str(key),str(val)) for (key,val) in kw.items()])

def listdir_full(dirname):
    return [join(dirname,entry) for entry in os.listdir(dirname)]

def first(tup):
    return tup[0]
def second(tup):
    return tup[1]

TAB_LEVEL = 0
def verbose(fn):
    @functools.wraps(fn)
    def new_ver(*args,**kw):
        global TAB_LEVEL
        print("\t"*TAB_LEVEL+"%s(%s)"%(fn.__name__,disp_args(*args,**kw)))
        TAB_LEVEL += 1
        result = fn(*args,**kw)
        TAB_LEVEL -= 1            
        print("\t"*TAB_LEVEL+"=> %s"%str(result))    
        return result
    return new_ver

CACHES = {}
CACHE_VERSIONS = {}
def memoized(fn):
    cache = {}
    CACHES[fn.__name__] = cache
    @functools.wraps(fn)
    def memo_ver(*args):
        if args in cache: return cache[args]
        else:
            obj = cache[args] = fn(*args)
            return obj
    return memo_ver

def save_caches(ver_num):
    CACHE_VERSIONS[ver_num] = cP.dumps(CACHES) #deepcopy(CACHES)

def revert_caches(ver_num):
    global CACHES
    if ver_num in CACHE_VERSIONS:
        for fname,cache in CACHES.items():
            cache.clear()
            cache.update(cP.loads(CACHE_VERSIONS[ver_num])[fname])
    else:
        for cache in CACHES.values():
            cache.clear()
    
def mappend(fn,li):
    if len(li) == 0: return []
    return reduce(op.__concat__,map(fn,li))
            
def mapvals(dic,fn):
    newdic = {}
    for key,val in dic.items():
        newdic[key] = fn(val)
    return newdic
    
def increasing(a,b,c):
    return a<b and b<c

def irange(start,last):
    return range(start,last+1)

def find_first(fn,vals,default=None):
    for val in vals:
        if fn(val): return val
    return default




class bcolors:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'

color_seqs = dict(
    purple = '\033[95m',
    blue = '\033[94m',
    green = '\033[92m',
    yellow = '\033[93m',
    red = '\033[91m',
    end = '\033[0m')
    
def cprint(color="red",*args):
    print(color_seqs[color]+str(args)+color_seqs["end"])